//==================================================================
//  Copyright (C) 2006  Davide Pasca
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//==================================================================
/// @file
///
///
/// 
/// @author Davide Pasca
/// 
//==================================================================

#include "stdafx.h"
#include <string>
using std::string;
#include "memfile.h"
#include "RendShaders.h"
#include "RendDevice.h"
#include "RendShadersD3D.h"
#include "RendShadersD3D_inl.h"
#include "TextureBase.h"

#include "shaders/compiled/PointSpritePS.h"
#include "shaders/compiled/PointSpriteVS.h"

#include "shaders/hexed/HDRFunctions.hlsl.h"
#include "shaders/hexed/PointSpritePS.hlsl.h"
#include "shaders/hexed/PointSpriteVS.hlsl.h"
#include "shaders/hexed/StdTypes.hlsl.h"
#include "shaders/hexed/StdPrimPipePS.hlsl.h"
#include "shaders/hexed/StdPrimPipeVS.hlsl.h"


//==================================================================
static D3DVERTEXELEMENT9 _part_vert_decl_def_c1[3] =
{
	{ 0,  0, D3DDECLTYPE_FLOAT3,	D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
	{ 0, 12, D3DDECLTYPE_D3DCOLOR,	D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0 },
	D3DDECL_END()
};
//==================================================================
static D3DVERTEXELEMENT9 *_part_vert_decl_defsp[2] =
{
	_part_vert_decl_def_c1,
	_part_vert_decl_def_c1
};

//==================================================================
static void buildStdVertDefinition( D3DVERTEXELEMENT9 *dest_buffp, int tex, int col, int nor )
{
static D3DVERTEXELEMENT9 base_pos =	{ 0, 0, D3DDECLTYPE_FLOAT3,	 D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION,  0 };
static D3DVERTEXELEMENT9 base_tex =	{ 0, 1, D3DDECLTYPE_FLOAT2,	 D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD,  0 };
static D3DVERTEXELEMENT9 base_col =	{ 0, 2, D3DDECLTYPE_D3DCOLOR,D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR,		0 };
static D3DVERTEXELEMENT9 base_nor =	{ 0, 3, D3DDECLTYPE_FLOAT3,	 D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL,    0 };
static D3DVERTEXELEMENT9 base_end =	D3DDECL_END();

	int	off = 0;
	dest_buffp[0] = base_pos;	off += 3 * sizeof(float); dest_buffp += 1;

	if ( tex )
	{
		dest_buffp[0] = base_tex;	dest_buffp[0].Offset = off; off += 2 * sizeof(float);
		++dest_buffp;
	}

	if ( col )
	{
		dest_buffp[0] = base_col;	dest_buffp[0].Offset = off; off += sizeof(D3DCOLOR);
		++dest_buffp;
	}

	if ( nor )
	{
		dest_buffp[0] = base_nor;	dest_buffp[0].Offset = off; off += 3 * sizeof(float);
		++dest_buffp;
	}

	dest_buffp[0] = base_end;
}

//==================================================================
/// Simple D3D File Include
//==================================================================
class SimpleD3DFileInclude : public ID3DXInclude
{
	safe_ptr<MemFile>	memfp;
	char				_datadir[1024];
public:
	//==================================================================
	SimpleD3DFileInclude( const char *datadirp )
	{
		ksys_strcpy( _datadir, datadirp ,sizeof(_datadir) );
	}

	//==================================================================
	HRESULT	Open( D3DXINCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID* ppData, UINT* pBytes )
	{
		char	buff[1500];

		ksys_strcpy( buff, _datadir, sizeof(buff)-500 );
		strcat( buff, pFileName );

		try {	memfp = new MemFile( buff );	} catch(...) {	return -1;	}

		*ppData = (VOID *)memfp->GetData();
		*pBytes = memfp->GetDataSize()-1;

		return 0;
	}

	//==================================================================
	HRESULT	Close( LPCVOID pData )
	{
		SAFE_DELETE( memfp );
		return 0;
	}
};

//==================================================================
/// Simple D3D Mem Include
//==================================================================
class SimpleD3DMemInclude : public ID3DXInclude
{
	static const int	MAX_INCLUDES = 32;

	struct Include
	{
		const char	*_namep;
		const void	*_datap;
		int			_data_size;
	};

	Include			_includes[MAX_INCLUDES];
	int				_n_includes;
public:
	//==================================================================
	SimpleD3DMemInclude()
	{
		_n_includes = 0;
	}

	//==================================================================
	void AddInclude( const char *fnamep, const void *datap, int data_size )
	{
		if ERR_FALSE( _n_includes < MAX_INCLUDES )
		{
			throw "Geeezz !!!";
		}
		int	i = _n_includes++;
		_includes[i]._namep = fnamep;
		_includes[i]._datap = datap;
		_includes[i]._data_size = data_size;
	}

	//==================================================================
	HRESULT	Open(	D3DXINCLUDE_TYPE IncludeType,
					LPCSTR pFileName,
					LPCVOID pParentData,
					LPCVOID *ppData,
					UINT *pBytes,
					LPSTR pFullPath,
					DWORD cbFullPath )
	{
		for (int i=0; i < _n_includes; ++i)
		{
			if NOT( stricmp( pFileName, _includes[i]._namep ) )
			{
				*ppData = (VOID *)_includes[i]._datap;
				*pBytes = _includes[i]._data_size;

				return 0;
			}
		}

		return -1;
	}

	//==================================================================
	HRESULT	Close( LPCVOID pData )
	{
		return 0;
	}
};


//==================================================================
static D3DVertexShader *createD3DVertexShader( const MemFile &memf, const char *entrypointp,
											   D3DDevice	*d3ddevp,
											   ID3DXInclude	*d3dincp )
{
	com_ptr<ID3DXBuffer>		code_buffp;
	com_ptr<ID3DXBuffer>		errormsg_buffp;
	IDirect3DVertexShader9		*shaderp;
	HRESULT						hr;

	hr = D3DXCompileShader( (const char *)memf.GetData(), memf.GetDataSize()-1,
							NULL,
							d3dincp, 
							entrypointp, 
							"vs_3_0", 
							0, 
							&code_buffp, 
							&errormsg_buffp, 
							NULL );

	if ( FAILED(hr) )
	{
		if ( errormsg_buffp )
			KSYS_DEBUG_PRINTF( "%s", (const char *)errormsg_buffp->GetBufferPointer() );

		throw "createD3DVertexShader failed";
	}

	d3ddevp->CreateVertexShader( (DWORD *)code_buffp->GetBufferPointer(), &shaderp );

	return shaderp;
}

//==================================================================
static D3DPixelShader *createD3DPixelShader( const MemFile &memf, const char *entrypointp,
											 D3DDevice		*d3ddevp,
											 ID3DXInclude	*d3dincp )
{
	com_ptr<ID3DXBuffer>		code_buffp;
	com_ptr<ID3DXBuffer>		errormsg_buffp;
	IDirect3DPixelShader9		*shaderp;
	HRESULT						hr;

	hr = D3DXCompileShader( (const char *)memf.GetData(), memf.GetDataSize()-1,
							NULL,
							d3dincp, 
							entrypointp, 
							"ps_3_0", 
							0, 
							&code_buffp, 
							&errormsg_buffp, 
							NULL );

	if ( FAILED(hr) )
	{
		if ( errormsg_buffp )
			KSYS_DEBUG_PRINTF( "%s\n", (const char *)errormsg_buffp->GetBufferPointer() );

		throw "createD3DPixelShader() failed";
	}

	d3ddevp->CreatePixelShader( (DWORD *)code_buffp->GetBufferPointer(), &shaderp );

	return shaderp;
}

//==================================================================
D3DVertexShader *RendShadersD3D::CreateD3DVertexShader( const MemFile &memf, const char *entrypointp )
{
	SimpleD3DMemInclude	simple_d3dinc;

	simple_d3dinc.AddInclude( "StdTypes.hlsl", _StdTypes_hlsl, sizeof(_StdTypes_hlsl)-1 );

	com_ptr<ID3DXBuffer>		code_buffp;
	com_ptr<ID3DXBuffer>		errormsg_buffp;
	IDirect3DVertexShader9		*shaderp;
	HRESULT						hr;

	hr = D3DXCompileShader( (const char *)memf.GetData(), memf.GetDataSize()-1,
							NULL,
							&simple_d3dinc, 
							entrypointp, 
							"vs_3_0",
							0, 
							&code_buffp, 
							&errormsg_buffp, 
							NULL );

	if ( FAILED(hr) )
	{
		if ( errormsg_buffp )
			KSYS_DEBUG_PRINTF( "%s", (const char *)errormsg_buffp->GetBufferPointer() );

		throw "CreateD3DVertexShader failed";
	}

	_d3ddevp->CreateVertexShader( (DWORD *)code_buffp->GetBufferPointer(), &shaderp );

	return shaderp;
}

//==================================================================
D3DPixelShader *RendShadersD3D::CreateD3DPixelShader( const MemFile &memf, const char *entrypointp )
{
	SimpleD3DMemInclude	simple_d3dinc;
	simple_d3dinc.AddInclude( "StdTypes.hlsl", _StdTypes_hlsl, sizeof(_StdTypes_hlsl)-1 );

	com_ptr<ID3DXBuffer>		code_buffp;
	com_ptr<ID3DXBuffer>		errormsg_buffp;
	IDirect3DPixelShader9		*shaderp;
	HRESULT						hr;

	hr = D3DXCompileShader( (const char *)memf.GetData(), memf.GetDataSize()-1,
							NULL,
							&simple_d3dinc, 
							entrypointp, 
							"ps_3_0",
							0, 
							&code_buffp, 
							&errormsg_buffp, 
							NULL );

	if ( FAILED(hr) )
	{
		if ( errormsg_buffp )
			KSYS_DEBUG_PRINTF( "%s", (const char *)errormsg_buffp->GetBufferPointer() );

		throw "CreateD3DPixelShader failed";
	}

	_d3ddevp->CreatePixelShader( (DWORD *)code_buffp->GetBufferPointer(), &shaderp );

	return shaderp;
}

//==================================================================
RendShadersD3D::RendShadersD3D(void) :
	_d3ddevp(NULL),
	_superp(NULL),
	_cur_vert_declp(NULL),
	_cur_vert_shaderp(NULL),
	_cur_pix_shaderp(NULL)
{
}

//==================================================================
void RendShadersD3D::Initialize( RendShaders *superp, const char *datadirp )
{
	char	fname[1024];

	_superp = superp;
	_d3ddevp = _superp->_rdevicep->GetD3DDevice();

	SimpleD3DMemInclude	simple_d3dinc;
	simple_d3dinc.AddInclude( "StdTypes.hlsl", _StdTypes_hlsl, sizeof(_StdTypes_hlsl)-1 );

	// build standard vertex shaders
	//sprintf( fname, "%sshaders_xe\\StdPrimPipeVS.hlsl", _datadir );
	//MemFile	memf( fname );
/*
	string	vs_source_str(	string( (const char *)_StdTypes_hlsl) + 
							string( (const char *)_StdPrimPipeVS_hlsl ) );

	MemFile	memf( vs_source_str.c_str(), strlen(vs_source_str.c_str()) );
*/
	MemFile	memf( (const void *)_StdPrimPipeVS_hlsl, sizeof(_StdPrimPipeVS_hlsl) );
	for (int tex=0; tex < 2; ++tex)
	{
		for (int col=0; col < 2; ++col)
		{
			for (int nor=0; nor < 2; ++nor)
			{
				char	vs_entry_point[128];
				RendShaders::MakeStdVSName_s( vs_entry_point, tex, col, nor );

				_std_vs_programp[tex][col][nor] = createD3DVertexShader( memf, vs_entry_point, _d3ddevp, &simple_d3dinc );

				D3DVERTEXELEMENT9	vert_def_buff[16];
				buildStdVertDefinition( vert_def_buff, tex, col, nor );
				HRESULT hres = _d3ddevp->CreateVertexDeclaration( vert_def_buff, &_std_vert_declp[tex][col][nor] );
				if ( hres < 0 )
				{
					KASSERT( 0 );
					throw;
				}
			}
		}
	}

	// build standard pixel shaders
/*
	string	ps_source_str(	string( (const char *)_StdTypes_hlsl) + 
							string( (const char *)_StdPrimPipePS_hlsl ) );

	MemFile	std_ps_memf( ps_source_str.c_str(), strlen(ps_source_str.c_str()) );
*/
	MemFile	std_ps_memf( (const void *)_StdPrimPipePS_hlsl, sizeof(_StdPrimPipePS_hlsl) );
	for (int tex=0; tex < 2; ++tex)
	{
		for (int nor=0; nor < 2; ++nor)
		{
			char	ps_entry_point[128];
			RendShaders::MakeStdPSName_s( ps_entry_point, tex, nor );
			_std_ps_programp[tex][nor] = createD3DPixelShader( std_ps_memf, ps_entry_point, _d3ddevp, &simple_d3dinc );
		}
	}

//	sprintf( fname, "%sshaders_xe\\PointSpriteVS.hlsl", datadirp );
//	MemFile	pntsprt_memf( fname );

	//for (int col=0; col < 2; ++col)
	{
		_d3ddevp->CreateVertexDeclaration( _part_vert_decl_defsp[0], &_part_vert_declarationsp[0] );
		_d3ddevp->CreateVertexShader( (DWORD*)g_xvs_PointSpriteVS, &_part_vert_shadersp[0] );
	}

	_d3ddevp->CreatePixelShader( (DWORD*)g_xps_PointSpritePS, &_part_pix_shadersp[0] );

	//-----------------------------------
	MemFile	hdr_func_memf( _HDRFunctions_hlsl, sizeof(_HDRFunctions_hlsl) );
	_HDRFunc_T1_C1_VS			= createD3DVertexShader( hdr_func_memf, "HDRFunc_T1_C1_VS", _d3ddevp, NULL );
	_HDRFunc_DS4x4Clamp_PS		= createD3DPixelShader( hdr_func_memf, "HDRFunc_DS4x4Clamp_PS", _d3ddevp, NULL );
	_HDRFunc_PlainTexture_PS	= createD3DPixelShader( hdr_func_memf, "HDRFunc_PlainTexture_PS", _d3ddevp, NULL );
	_HDRFunc_Bloom_PS			= createD3DPixelShader( hdr_func_memf, "HDRFunc_Bloom_PS", _d3ddevp, NULL );
	_HDRFunc_FinalPass_PS		= createD3DPixelShader( hdr_func_memf, "HDRFunc_FinalPass_PS", _d3ddevp, NULL );
}

//==================================================================
RendShadersD3D::~RendShadersD3D()
{

}

//==================================================================
void RendShadersD3D::BeginRender()
{
	_cur_vert_declp = NULL;
	_cur_vert_shaderp = NULL;
	_cur_pix_shaderp = NULL;
}

//==================================================================
void RendShadersD3D::EndRender()
{
	_cur_vert_declp = NULL;
	_cur_vert_shaderp = NULL;
	_cur_pix_shaderp = NULL;
}

//==================================================================
void RendShadersD3D::ResetState()
{
}

//==================================================================
void RendShadersD3D::SelectStdVShader( int tex, int col, int nor )
{
	D3DVertexShader			*vert_shap	= _std_vs_programp[tex][col][nor];
	D3DPixelShader			*pix_shap	= _std_ps_programp[tex][nor];

	if ( vert_shap != _cur_vert_shaderp )
		_d3ddevp->SetVertexShader( _cur_vert_shaderp = vert_shap );

	if ( pix_shap != _cur_pix_shaderp )
		_d3ddevp->SetPixelShader( _cur_pix_shaderp = pix_shap );
}

//==================================================================
void RendShadersD3D::SelectPntSprtShader( int col, float max_size )
{
	D3DVertexDeclaration	*vert_decp	= _part_vert_declarationsp[0];
	D3DVertexShader			*vert_shap	= _part_vert_shadersp[0];
	D3DPixelShader			*pix_shap	= _part_pix_shadersp[0];

	if ( vert_decp != _cur_vert_declp )
		_d3ddevp->SetVertexDeclaration( _cur_vert_declp = vert_decp );

	if ( vert_shap != _cur_vert_shaderp )
		_d3ddevp->SetVertexShader( _cur_vert_shaderp = vert_shap );

	if ( pix_shap != _cur_pix_shaderp )
		_d3ddevp->SetPixelShader( _cur_pix_shaderp = pix_shap );
}

//==================================================================
static void calcSampOffsetDownScale4x4( float src_wd, float src_he, Vector4 *pvSampleOffsets )
{
    // Sample from the 16 surrounding points. Since the center point will be in the
    // exact center of 16 texels, a 1.5f offset is needed to specify a texel center.
    for( int y = 0; y < 4; y++ )
    {
        for( int x = 0; x < 4; x++ )
        {
            pvSampleOffsets->_x = ((FLOAT)x-1.5f) / src_wd;
            pvSampleOffsets->_y = ((FLOAT)y-1.5f) / src_he;
            pvSampleOffsets++;
        }
    }
}

//==================================================================
void RendShadersD3D::SelectDownsample4x4ClampShader( u_int src_wd, u_int src_he )
{
	Vector4	samp_offs[16];

	calcSampOffsetDownScale4x4( src_wd, src_he, samp_offs );

	if ( _HDRFunc_T1_C1_VS != _cur_vert_shaderp )
		_d3ddevp->SetVertexShader( _cur_vert_shaderp = _HDRFunc_T1_C1_VS );

	if ( _HDRFunc_DS4x4Clamp_PS != _cur_pix_shaderp )
		_d3ddevp->SetPixelShader( _cur_pix_shaderp = _HDRFunc_DS4x4Clamp_PS );

	_d3ddevp->SetPixelShaderConstantF( RendShaders::_FSP_HDRF_sample_offsets,
										(const float *)samp_offs, 16 );
}

//==================================================================
void RendShadersD3D::SelectPlainTextureShader()
{
	if ( _HDRFunc_T1_C1_VS != _cur_vert_shaderp )
		_d3ddevp->SetVertexShader( _cur_vert_shaderp = _HDRFunc_T1_C1_VS );

	if ( _HDRFunc_PlainTexture_PS != _cur_pix_shaderp )
		_d3ddevp->SetPixelShader( _cur_pix_shaderp = _HDRFunc_PlainTexture_PS );
}

//==================================================================
void RendShadersD3D::SelectHDRFinalPassShader( const Vector4 &bloom_scale )
{
	if ( _HDRFunc_T1_C1_VS != _cur_vert_shaderp )
		_d3ddevp->SetVertexShader( _cur_vert_shaderp = _HDRFunc_T1_C1_VS );

	if ( _HDRFunc_FinalPass_PS != _cur_pix_shaderp )
		_d3ddevp->SetPixelShader( _cur_pix_shaderp = _HDRFunc_FinalPass_PS );

	_d3ddevp->SetPixelShaderConstantF( RendShaders::_FSP_HDRF_bloom_scale, bloom_scale._v, 1 );
}

//==================================================================
void RendShadersD3D::SelectBloomShader( const Vector4 *samp_offsp,
										const Vector4 *samp_weightsp,
										float bright_threshold )
{
	if ( _HDRFunc_T1_C1_VS != _cur_vert_shaderp )
		_d3ddevp->SetVertexShader( _cur_vert_shaderp = _HDRFunc_T1_C1_VS );

	if ( _HDRFunc_Bloom_PS != _cur_pix_shaderp )
		_d3ddevp->SetPixelShader( _cur_pix_shaderp = _HDRFunc_Bloom_PS );

	_d3ddevp->SetPixelShaderConstantF( RendShaders::_FSP_HDRF_sample_offsets, (const float *)samp_offsp, 16 );
	_d3ddevp->SetPixelShaderConstantF( RendShaders::_FSP_HDRF_sample_weights, (const float *)samp_weightsp, 16 );
	_d3ddevp->SetPixelShaderConstantF( RendShaders::_FSP_HDRF_bright_threshold,	Vector4( bright_threshold,
																						 bright_threshold,
																						 bright_threshold,
																						 bright_threshold )._v, 1 );
}

//==================================================================
void RendShadersD3D::SelectVertexStream( void *stream_posp, void *stream_texp, void *stream_colp, void *stream_norp, int stride )
{
}
